home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / c / tags.c < prev    next >
Text File  |  1986-07-12  |  74KB  |  2,915 lines

  1. /*
  2.  
  3. Date: Thu 10 Jul 86 03:07:25-EDT
  4. From: Joseph M. Newcomer <Joe.Newcomer@C.CS.CMU.EDU>
  5. Subject: TAGS.C Added to Library
  6.  
  7.  
  8. I now have a working TAGS system for C and Epsilon.  For those of you
  9. who don't know what TAGS is, it allows you to find the file which
  10. contains a definition if you know the name of the object being defined.
  11. For example, you are looking at a call on procedure xYzzY, and want to
  12. see what it does.  Fine.  Invoke TAGS by typing Alt-., give it the
  13. name xYzzY, and suddenly before your very orbs is the definition point
  14. for the function!  If the cursor is sitting somewhere before a word,
  15. that word is the default name to look up and so A-. CR usually suffices.
  16.  
  17. This requires running a program over your source file that locates all
  18. the tags and puts them in a tag dictionary, then having an editor command
  19. that looks things up in the tag dictionary.  I am submitting the source
  20. for both of these.  TAGS.C is the name of the file, and it contains
  21. its makefile and the editor support for Epsilon, TAGS.E and TAGS1.E.
  22. Tags is non-resident and loaded by a library load operation; included is
  23. the procedure to do the library load, which you can install in your
  24. Epsilon environment somewhere (I put mine in FILES.E).
  25.  
  26. There are some caveats.  I didn't write this, it was written by Richard
  27. M. Stallman of EMACS and GNU fame.  It comes with his copyright, which
  28. I believe I have honored in providing full support source with it (namely
  29. Epsilon code).  Please continue to honor this copyright.  I added
  30. Scribe and TeX modes, but have only tested them minimally.  I have not
  31. tested the Fortran or Lisp modes at all.  .C and .E files seem to work.
  32. Feel free to enhance it.  It seems to have various obscure switch options
  33. which I don't understand; I've inserted documentation showing the result
  34. of the various switches.  I've also added a mode that allows it to 
  35. interact reasonably with makefiles; this is documented in the comments.
  36.  
  37. I have found it immensley useful after I went back to my 84-file 700,000 byte
  38. source after 6 months' absence; I can now find things.  I offer it, 
  39. unsupported and 'as is'.  If it is broken, please feel free to make
  40. improvments and resubmit it.
  41.                     joe
  42. -------
  43.  
  44. Received: from PREP.AI.MIT.EDU by A.SEI.CMU.EDU; 29 May 86 19:02:21 EDT
  45. Received: by PREP.AI.MIT.EDU; Thu, 29 May 86 19:02:12 EDT
  46. Date: Thu, 29 May 86 19:02:12 EDT
  47. From: rms@PREP.AI.MIT.EDU (Richard M. Stallman)
  48. Message-Id: <8605292302.AA01542@prep.ai.mit.edu>
  49. To: Joe.Newcomer
  50. In-Reply-To: Joe.Newcomer's message of Wednesday, 28 May 1986 18:42:06 EDT
  51. Subject: tags
  52. */
  53.  
  54. /* Tags file maker to go with GNUmacs
  55.    Copyright (C) 1984 Richard M. Stallman and Ken Arnold
  56.  
  57. This program is distributed in the hope that it will be useful,
  58. but without any warranty.  No author or distributor
  59. accepts responsibility to anyone for the consequences of using it
  60. or for whether it serves any particular purpose or works at all,
  61. unless he says so in writing.
  62.  
  63.    Permission is granted to anyone to distribute verbatim copies
  64.    of this program's source code as received, in any medium, provided that
  65.    the copyright notice, the nonwarraty notice above
  66.    and this permission notice are preserved,
  67.    and that the distributor grants the recipient all rights
  68.    for further redistribution as permitted by this notice,
  69.    and informs him of these rights.
  70.  
  71.    Permission is granted to distribute modified versions of this
  72.    program's source code, or of portions of it, under the above
  73.    conditions, plus the conditions that all changed files carry
  74.    prominent notices stating who last changed them and that the
  75.    derived material, including anything packaged together with it and
  76.    conceptually functioning as a modification of it rather than an
  77.    application of it, is in its entirety subject to a permission
  78.    notice identical to this one.
  79.  
  80.    Permission is granted to distribute this program (verbatim or
  81.    as modified) in compiled or executable form, provided verbatim
  82.    redistribution is permitted as stated above for source code, and
  83.     A.  it is accompanied by the corresponding machine-readable
  84.       source code, under the above conditions, or
  85.     B.  it is accompanied by a written offer, with no time limit,
  86.       to distribute the corresponding machine-readable source code,
  87.       under the above conditions, to any one, in return for reimbursement
  88.       of the cost of distribution.   Verbatim redistribution of the
  89.       written offer must be permitted.  Or,
  90.     C.  it is distributed by someone who received only the
  91.       compiled or executable form, and is accompanied by a copy of the
  92.       written offer of source code which he received along with it.
  93.  
  94.    Permission is granted to distribute this program (verbatim or as modified)
  95.    in executable form as part of a larger system provided that the source
  96.    code for this program, including any modifications used,
  97.    is also distributed or offered as stated in the preceding paragraph.
  98.  
  99. In other words, you are welcome to use, share and improve this program.
  100. You are forbidden to forbid anyone else to use, share and improve
  101. what you give them.   Help stamp out software-hoarding!  */
  102.  
  103. /*
  104.  
  105. Converted to Lattice C 3.0 for MS/PC DOS by Joseph M. Newcomer
  106. June, 1986.
  107.  
  108. Copyright 1986, Joseph M. Newcomer.
  109.  
  110. Subject to the same copyright and distribution agreement as stated above.
  111. Non-hoardable software.
  112.  
  113. Change notes:
  114.  
  115. Several changes were required to convert this to Lattice C and MS/PC DOS.
  116. In particular, the 'system' command and case sensitivity in files do
  117. not exist and had to be replaced.  Filename expansion is not implicitly
  118. done on the DOS command line and had to be explicitly installed.
  119.  
  120. */
  121.  
  122. /*****************************************************************************
  123. User documentation
  124.  
  125. invoke as
  126.     tags [options] filename filename ...
  127.  
  128. where each filename may be a DOS wildcard pattern filename.
  129.  
  130. The options are:
  131.  
  132.     -e    Produces mapping of name --> line,char
  133.     -v    Produces mapping of name --> file ((line+63) / 64)
  134.     -x    Produces human-readable xref  name -> line file declaration
  135.     none of the above
  136.         Produces format suitable for Epsilon 'tags' mode
  137.  
  138.     -t    include typedef symbols; 
  139.  
  140.     -a    appends current input result to 'tags' file
  141.  
  142.     -u    updates existing 'tags' file (removes previous entry and
  143.         appends new result on end)
  144.  
  145.     -B    sets search delimiter to '?'
  146.     
  147.     -F    sets search delimiter to '/' (default)
  148.  
  149.     -w    suppresses warnings
  150.  
  151.     -m    Writes .tag file (empty file to be used by makefile
  152.         to regenerate tags that have changed)
  153.     
  154.  
  155. Normal use is one of the following forms
  156.  
  157.     tags file file file ...
  158.         Creates 'tags' from the files listed, suitable for Epsilon
  159.         tags-mode
  160.  
  161.     tags -u file1 file2 ...
  162.         Replaces the entries for file1, file2, etc. in 'tags' (or adds
  163.         them if they are not present)
  164.  
  165.     tags -m -u file1 
  166.         Replaces the entries for file1 in 'tags' and
  167.         updates the .tag file.  This line would typically appear
  168.         in the makefile as
  169.  
  170. .tag.c :
  171.     tags -m -u $<
  172.  
  173.     
  174. Included at the end of this listing is the makefile for tags itself.
  175.  
  176.  
  177. *****************************************************************************/
  178.  
  179. /*
  180. Documentation of output file format:
  181.  
  182.     -e flag
  183.  
  184.     ^L
  185.         file_name,number_of_entries
  186.     name\0177line,char
  187.     name\0177line,char
  188.     ...
  189. -----------------------------------------------------------------------------
  190. -v
  191. name file (line+63)/64
  192.  
  193. D:\TAGS>tags -v junk.c
  194. C_entries junk.c 2
  195. L_getit junk.c 3
  196. PF_funcs junk.c 3
  197. add_node junk.c 2
  198. begtoken junk.c 1
  199. concat junk.c 3
  200. consider_token junk.c 2
  201. endtoken junk.c 1
  202. error junk.c 3
  203. fatal junk.c 3
  204. find_entries junk.c 2
  205. free_tree junk.c 2
  206. getit junk.c 3
  207. getline junk.c 3
  208. init junk.c 2
  209. initbuffer junk.c 3
  210. intoken junk.c 1
  211. isgood junk.c 1
  212. iswhite junk.c 1
  213. main junk.c 2
  214. max junk.c 1
  215. pfnote junk.c 2
  216. put_entries junk.c 2
  217. readline junk.c 3
  218. rindex junk.c 3
  219. savestr junk.c 3
  220. tail junk.c 3
  221. takeprec junk.c 3
  222. total_size_of_entries junk.c 2
  223. xmalloc junk.c 3
  224. xrealloc junk.c 3
  225.  
  226. D:\TAGS>
  227. -----------------------------------------------------------------------------
  228. -x
  229. name            line file             definition
  230.  
  231. D:\TAGS>tags -x junk.c
  232. C_entries        124 junk.c           C_entries ()
  233. L_getit          143 junk.c           L_getit()
  234. PF_funcs         133 junk.c           PF_funcs(fi)
  235. add_node         103 junk.c           add_node(node, cur_node)
  236. begtoken          16 junk.c           #define    begtoken(arg)    (_btk[arg])
  237. concat           169 junk.c           concat (s1, s2, s3)
  238. consider_token   126 junk.c           consider_token (lpp, token, f)
  239. endtoken          18 junk.c           #define    endtoken(arg)    (_etk[arg])
  240. error            165 junk.c           void error (s1, s2)
  241. fatal            162 junk.c           fatal (s1, s2)
  242. find_entries      89 junk.c           void find_entries (file)
  243. free_tree        100 junk.c           void free_tree(node)
  244. getit            141 junk.c           getit()
  245. getline          130 junk.c           void getline (atchar)
  246. init              87 junk.c           init()
  247. initbuffer       146 junk.c           initbuffer (linebuffer)
  248. intoken           17 junk.c           #define    intoken(arg)    (_itk[arg])
  249. isgood            19 junk.c           #define    isgood(arg)    (_gd[arg])
  250. iswhite           15 junk.c           #define    iswhite(arg)    (_wht[arg])
  251. main              83 junk.c           main(ac,av)
  252. max               20 junk.c           #define    max(I1,I2)    (I1 > I2 ? I1 : I2)
  253. pfnote            92 junk.c           pfnote (name, f, linestart, linelen, lno, cno)
  254. put_entries      106 junk.c           put_entries(node)
  255. readline         150 junk.c           readline (linebuffer, stream)
  256. rindex           159 junk.c           rindex(sp, c)
  257. savestr          155 junk.c           savestr(cp)
  258. tail             136 junk.c           tail(cp)
  259. takeprec         139 junk.c           takeprec()
  260. total_size_of_entries 109 junk.c           int total_size_of_entries(node)
  261. xmalloc          173 junk.c           xmalloc (size)
  262. xrealloc         177 junk.c           xrealloc (ptr, size)
  263.  
  264. If the -e flag is given, it dominates everything
  265. If the -e flag is not given and the -x flag is not given, we
  266. get search patterns
  267.  
  268. If the -e flag is not given and the -v flag is not given, 
  269.  
  270. D:\TAGS>tags junk.c
  271.  
  272. produces no output but writes the file 'tags':
  273.  
  274. ^L
  275. junk.c,604
  276. C_entries124,3669
  277. L_getit143,3902
  278. PF_funcs133,3809
  279. add_node103,3310
  280. #define    begtoken16,428
  281. concat169,4282
  282. consider_token126,3687
  283. #define    endtoken18,561
  284. void error165,4228
  285. fatal162,4187
  286. void find_entries89,3026
  287. void free_tree100,3266
  288. getit141,3889
  289. void getline130,3763
  290. init87,3014
  291. initbuffer146,3923
  292. #define    intoken17,495
  293. #define    isgood19,624
  294. #define    iswhite15,368
  295. main83,2965
  296. #define    max20,689
  297. pfnote92,3074
  298. put_entries106,3368
  299. readline150,3995
  300. rindex159,4140
  301. savestr155,4099
  302. tail136,3843
  303. takeprec139,3873
  304. int total_size_of_entries109,3413
  305. xmalloc173,4341
  306. xrealloc177,4390
  307.  
  308. -----------------------------------------------------------------------------
  309. tags -t
  310.  
  311. produces no output but writes file 'tags':
  312. C_entries    junk.c    /^C_entries ()$/
  313. L_getit    junk.c    /^L_getit()$/
  314. Mjunk    junk.c    /^main(ac,av)$/
  315. NODE    junk.c    32
  316. PF_funcs    junk.c    /^PF_funcs(fi)$/
  317. TYST    junk.c    36
  318. add_node    junk.c    /^add_node(node, cur_node)$/
  319. begtoken    junk.c    /^#define    begtoken(arg)    (_btk[arg])    \
  320. concat    junk.c    /^concat (s1, s2, s3)$/
  321. consider_token    junk.c    /^consider_token (lpp, token, f)$/
  322. endtoken    junk.c    /^#define    endtoken(arg)    (_etk[arg])    \
  323. error    junk.c    /^void error (s1, s2)$/
  324. fatal    junk.c    /^fatal (s1, s2)$/
  325. find_entries    junk.c    /^void find_entries (file)$/
  326. free_tree    junk.c    /^void free_tree(node)$/
  327. getit    junk.c    /^getit()$/
  328. getline    junk.c    /^void getline (atchar)$/
  329. init    junk.c    /^init()$/
  330. initbuffer    junk.c    /^initbuffer (linebuffer)$/
  331. intoken    junk.c    /^#define    intoken(arg)    (_itk[arg])    \
  332. isgood    junk.c    /^#define    isgood(arg)    (_gd[arg])    \
  333. iswhite    junk.c    /^#define    iswhite(arg)    (_wht[arg])    \
  334. max    junk.c    /^#define    max(I1,I2)    (I1 > I2 ? I1 : I2)$/
  335. pfnote    junk.c    /^pfnote (name, f, linestart, linelen, lno, cno)$/
  336. put_entries    junk.c    /^put_entries(node)$/
  337. readline    junk.c    /^readline (linebuffer, stream)$/
  338. rindex    junk.c    /^rindex(sp, c)$/
  339. savestr    junk.c    /^savestr(cp)$/
  340. tail    junk.c    /^tail(cp)$/
  341. takeprec    junk.c    /^takeprec()$/
  342. total_size_of_entries    junk.c    /^int total_size_of_entries(node)$/
  343. xmalloc    junk.c    /^xmalloc (size)$/
  344. xrealloc    junk.c    /^xrealloc (ptr, size)$/
  345.  
  346. -----------------------------------------------------------------------------
  347. */
  348.  
  349. /*****************************************************************************
  350. *           Change Log
  351. *  Date     | Change
  352. *-----------+-----------------------------------------------------------------
  353. *  2-Jun-86 | Created
  354. *  2-Jun-86 | reg => (nothing) since register variables don't buy all that
  355. *           | much on an 80286, and most compilers have bugs with them
  356. *  2-Jun-86 | logical is now unsigned char
  357. *  2-Jun-86 | Added void to valueless procedures
  358. *  2-Jun-86 | replaced several 'register' declarations with 'reg'
  359. *  2-Jun-86 | xmalloc changed from int to char* so it works properly on
  360. *           | 80286 and friends in all memory models
  361. *  2-Jun-86 | xmalloc takes unsigned arg, like malloc
  362. *  2-Jun-86 | xrealloc also changed consistent with xmalloc.  Only problem
  363. *           | is, I can't find out what realloc does...
  364. *  2-Jun-86 | include <> => include ""
  365. *  2-Jun-86 | Wrote a realloc routine which is sort of what is supposed
  366. *           | to be done, I think
  367. *  2-Jun-86 | 'entry' => 'entry_name', reserved word in Lattice C
  368. *  2-Jun-86 | Added some forward (extern) declarations
  369. *  2-Jun-86 | Added explict case to return of readline
  370. *  2-Jun-86 | Looks like Lattice C has undocumented realloc function.
  371. *           | Comment out my implementation
  372. *  2-Jun-86 | Added debug ctce and do some debug output
  373. *  3-Jun-86 | readline now returns the file position, not the number of chars
  374. *           | read.  Appropriate changes have been made at all sites that
  375. *           | call readline.  This is because of CRLF -> LF translation by
  376. *           | C runtime.  Note that Lattice C 3.0 provides correct file
  377. *           | position even when translation is done; earlier Lattice C
  378. *           | versions did not.
  379. *  3-Jun-86 | In C functions, pfnote adjusts endpos-1 (probably caused by
  380. *           | above change)
  381. *  3-Jun-86 | Remove code for -u, did Unix system call; will need to rewrite
  382. *           | later.
  383. *           | Do wildcard expansions inline, since we can't assume that the
  384. *           | commands are expanded; create new processing routine each_file
  385. *           | which takes the filename as input and is the body of the loop
  386. *           | formerly in 'main'
  387. *  3-Jun-86 | Quote patterns for Epsilon re_search
  388. *  3-Jun-86 | Changed pattern size from 50 to 40 due to apparent Epsilon
  389. *           | limitation on pattern size for re-s
  390. *  3-Jun-86 | Expand wildcards by using Lattice C 3.0 functions dfind, dnext
  391. *           | and strsfn (parses file names)
  392. *  4-Jun-86 | Always expand filename to full d:\path\file
  393. *  4-Jun-86 | Added code to support -u option
  394. *  4-Jun-86 | In changing name of 'main', search for \\, not /
  395. *  4-Jun-86 | Added Scribe/FinalWord mode
  396. *  4-Jun-86 | Annouce files and modes
  397. *  4-Jun-86 | Made duplicate tag message same format as standard error message
  398. *           | so find-next-error in editor will find it
  399. *  4-Jul-86 | Added -m flag for makefile processing
  400. *  4-Jul-86 | Removed realloc code, no longer needed
  401. *  4-Jul-86 | Added TeX mode, not tested since I have no TeX files today
  402. *  4-Jul-86 | Added 'void' to various void procedures to a-void compiler
  403. *           | warnings
  404. *  4-Jul-86 | Flush stderr when logging filenames
  405. *****************************************************************************/
  406.  
  407.  
  408. #include "stdio.h"
  409. #include "ctype.h"
  410. #include "string.h"
  411. #include "dos.h"
  412.  
  413. /* Define the symbol ETAGS to make the program "etags",
  414.  which makes emacs-style tag tables by default.
  415.  Define CTAGS to make the program "ctags" compatible with the usual one.
  416.  
  417.  Default is CTAGS = ETAGS = 0, which creates Epsilon tags.  */
  418.  
  419. /* #define ETAGS 1 */
  420.  
  421. #define    reg    
  422. #define    logical    unsigned char
  423.  
  424. #define debug FALSE
  425.  
  426. #define    TRUE    (1)
  427. #define    FALSE    (0)
  428.  
  429. #define    iswhite(arg)    (_wht[arg])    /* T if char is white        */
  430. #define    begtoken(arg)    (_btk[arg])    /* T if char can start token    */
  431. #define    intoken(arg)    (_itk[arg])    /* T if char can be in token    */
  432. #define    endtoken(arg)    (_etk[arg])    /* T if char ends tokens    */
  433. #define    isgood(arg)    (_gd[arg])    /* T if char can be after ')'    */
  434.  
  435. #define    max(I1,I2)    (I1 > I2 ? I1 : I2)
  436.  
  437. struct    nd_st {                /* sorting structure        */
  438.     char    *entry_name;        /* function or type name    */
  439.     char    *file;            /* file name            */
  440.     logical f;            /* use pattern or line no    */
  441.     int    lno;            /* line number tag is on    */
  442.     long    cno;            /* character number line starts on */
  443.     char    *pat;            /* search pattern        */
  444.     logical    been_warned;        /* set if noticed dup        */
  445.     struct    nd_st    *left,*right;    /* left and right sons        */
  446. };
  447.  
  448. long    ftell();
  449. typedef    struct    nd_st    NODE;
  450.  
  451. int number; /* tokens found so far on line starting with # (including #) */
  452. logical gotone,                /* found a func already on line    */
  453.                     /* boolean "func" (see init)    */
  454.     _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
  455.  
  456.     /* typedefs are recognized using a simple finite automata,
  457.      * tydef is its state variable.
  458.      */
  459. typedef enum {none, begin, middle, end } TYST;
  460.  
  461. TYST tydef = none;
  462.  
  463. char    searchar = '/';            /* use /.../ searches         */
  464.  
  465. int    lineno;            /* line number of current line */
  466. long    charno;            /* current character number */
  467. long    linecharno;        /* character number of start of line */
  468.  
  469. char    *curfile,        /* current input file name        */
  470.     *outfile= "tags",    /* output file                */
  471.     *white    = " \f\t\n",    /* white chars                */
  472.     *endtk    = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
  473.                 /* token ending chars            */
  474.     *begtk    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz",
  475.                 /* token starting chars            */
  476.     *intk    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789",
  477.                 /* valid in-token chars            */
  478.     *notgd    = ",;";        /* non-valid after-function chars    */
  479.  
  480. int    file_num;        /* current file number            */
  481. int    aflag;            /* -a: append to tags */
  482. int    tflag;            /* -t: create tags for typedefs */
  483. int    mflag;            /* -m: create .tag file for makefile */
  484. int    uflag;            /* -u: update tags */
  485. int    wflag;            /* -w: suppress warnings */
  486. int    vflag;            /* -v: create vgrind style index output */
  487. int    xflag;            /* -x: create cxref style output */
  488. int    eflag;            /* -e: emacs style output */
  489.  
  490. FILE    *inf,            /* ioptr for current input file        */
  491.     *outf;            /* ioptr for tags file            */
  492.  
  493. NODE    *head;            /* the head of the sorted binary tree    */
  494.  
  495. char    *savestr();
  496. char    *rindex();
  497. char *concat ();
  498. void initbuffer ();
  499. long readline ();
  500.  
  501. /* A `struct linebuffer' is a structure which holds a line of text.
  502.  `readline' reads a line from a stream into a linebuffer
  503.  and works regardless of the length of the line.  */
  504.  
  505. struct linebuffer
  506.   {
  507.     long size;
  508.     char *buffer;
  509.   };
  510.  
  511. struct linebuffer lb, lb1;
  512.  
  513. int needCRLF = FALSE;
  514.  
  515. #define forward extern
  516.  
  517. forward void add_node();
  518. forward void C_entries();
  519. forward void checkCRLF();
  520. forward void each_file();
  521. forward void error();
  522. forward void find_entries();
  523. forward void free_tree();
  524. forward void getit();
  525. forward void getline();
  526. forward void init();
  527. forward void L_funcs();
  528. forward void L_getit();
  529. forward void put_entries();
  530. forward void S_labels();
  531. forward void tagit();
  532. forward void takeprec();
  533. forward void T_labels();
  534. forward void usage();
  535. forward char * xmalloc();
  536. forward char * xrealloc();
  537. forward char * realloc();
  538. extern char * malloc();
  539. extern char * stpchr();
  540.  
  541. /****************************************************************************
  542. *                                    main
  543. * Inputs:
  544. *       int ac: count of arguments
  545. *    char * av[]: vector of pointers to arguments
  546. * Result: void
  547. *       Implicit value is returned via exit() call
  548. * Effect: 
  549. *       Executes the tags program
  550. ****************************************************************************/
  551.  
  552. void main(ac,av)
  553.      int    ac;
  554.      char    *av[];
  555. {
  556.   int i;
  557.  
  558. #ifdef ETAGS
  559.   eflag = 1;
  560. #endif
  561.  
  562.  
  563.   while (ac > 1 && av[1][0] == '-')
  564.     {
  565.       eflag = 0;
  566.       for (i=1; av[1][i]; i++)
  567.     {
  568.       switch(av[1][i])
  569.         {
  570.         case 'B':
  571.           searchar='?';
  572.           break;
  573.         case 'F':
  574.           searchar='/';
  575.           break;
  576.         case 'a':
  577.           aflag++;
  578.           break;
  579.         case 'e':
  580.           eflag++;
  581.           break;
  582.         case 'm':
  583.           mflag++;
  584.           break;
  585.         case 't':
  586.           tflag++;
  587.           break;
  588.         case 'u':
  589.           uflag++;
  590.           break;
  591.         case 'w':
  592.           wflag++;
  593.           break;
  594.         case 'v':
  595.           vflag++;
  596.           xflag++;
  597.           break;
  598.         case 'x':
  599.           xflag++;
  600.           break;
  601.         default:
  602.             usage(av[0]);
  603.         exit(1);
  604.         }
  605.     }
  606.       ac--; av++;
  607.     }
  608.  
  609.   if (ac <= 1)
  610.     {
  611.      usage(av[0]);
  612.      exit(1);
  613.     }
  614.  
  615.   if (eflag)
  616.     outfile = "TAGS";
  617.  
  618.   init();            /* set up boolean "functions"        */
  619.  
  620.   initbuffer (&lb);
  621.   initbuffer (&lb1);
  622.   /*
  623.    * loop through files finding functions
  624.    */
  625.   if (eflag)
  626.     {
  627.       outf = fopen (outfile, aflag ? "a" : "w");
  628.       if (!outf)
  629.     {
  630.       perror (outfile);
  631.       exit (1);
  632.     }
  633.     }
  634.  
  635.   for (file_num = 1; file_num < ac; file_num++)
  636.     {
  637.      /* Unix would expand the file names; in MS-DOS if we have wildcards
  638.     we have to expand them here.  This requires doing the expansion
  639.     and calling a per-file processing routine
  640.      */
  641.  
  642.      struct FILEINFO info;
  643.  
  644.      char fname[80];
  645.      char dev[3];
  646.      char path[FMSIZE];
  647.      char name[FNSIZE];
  648.      char ext[4];
  649.      int drive;
  650.  
  651.  
  652.      if(dfind(&info,av[file_num],0))
  653.         { /* no files match */
  654.      fprintf(stderr,"%s 0 Error: Could not find file\n",av[file_num]);
  655.      continue;
  656.     } /* no files match */
  657.  
  658.      /* Parse the filename into its device, path, filename and extension
  659.     components
  660.      */
  661.      strsfn(av[file_num],dev,path,name,ext);
  662.  
  663.      /* If no explicit path is given, get the current path */
  664.  
  665.      if(strlen(dev) == 0)
  666.         { /* default drive */
  667.     drive = getdsk() + 1;    /* current default drive's path */
  668.                 /* Note that A=0, B=1, etc., but
  669.                    we want it encoded as
  670.                        A=1, B=2, etc.
  671.                 */
  672.     dev[0] = drive + 'A' - 1;
  673.     dev[1] = ':';
  674.     dev[2] = '\0';
  675.     } /* default drive */
  676.      else
  677.     drive = dev[0] - 'A' + 1;
  678.  
  679.      if(strlen(path) == 0)
  680.         getcd(drive,path);
  681.  
  682.      do
  683.         { /* scan file name */
  684.  
  685.      /* Expand the filename by getting the device and path from the
  686.         input string and appending the filename from the info block
  687.      */
  688.  
  689.      strcpy(fname,dev);
  690.      if(strlen(path) > 0 && path[0] != '\\')
  691.         strcat(fname,"\\");
  692.      strcat(fname,path);
  693.      if(fname[strlen(fname)-1] != '\\')
  694.          strcat(fname,"\\");
  695.  
  696.      strcat(fname,info.name);
  697.      strlwr(fname);    /* make all lower case for readability */
  698.  
  699.      if(!xflag && !vflag) 
  700.         { /* announce */
  701.          fprintf(stderr,"%s",fname);
  702.          fflush(stderr);
  703.          needCRLF = TRUE;
  704.         } /* announce */
  705.  
  706.      each_file(fname);
  707.  
  708.      checkCRLF();
  709.  
  710.     } /* scan file name */
  711.          while (!dnext(&info));
  712.     }
  713.  
  714.     if (uflag)
  715.        {
  716.     /* This sequence can be omitted because we sort the listing
  717.        when we are in Epsilon
  718.     */
  719.     /****************************************************************
  720.     sprintf(cmd, "sort %s -o %s", outfile, outfile);
  721.     system(cmd);
  722.     ****************************************************************/
  723.        }
  724.  
  725.  
  726.   exit(0);
  727. }
  728.  
  729. /****************************************************************************
  730. *                                    usage
  731. * Inputs:
  732. *       char * name: Program name from argv[0]
  733. * Result: void
  734. *       
  735. * Effect: 
  736. *       Issues the 'usage' message
  737. * Notes:
  738. *    This depends upon the fact that DOS 3.x returns the program name as
  739. *    the first argument.  If it does not, you will have to change this
  740. *    routine
  741. ****************************************************************************/
  742.  
  743. void usage(name)
  744.     char * name;
  745.     {
  746.      char node[20];
  747.      char drive[4];
  748.      char path[80];
  749.      char ext[5];
  750.      strsfn(name,drive,path,node,ext);
  751.      fprintf(stderr,"Usage: %s [-BFaemtuwvx] file ...\n",node);
  752.     }
  753.  
  754. /****************************************************************************
  755. *                                  checkCRLF
  756. * Effect: 
  757. *       If a CRLF is needed on the output stream, puts one out.  Sets
  758. *    needCRLF to false
  759. ****************************************************************************/
  760.  
  761. void checkCRLF()
  762.     {
  763.      if(needCRLF) fprintf(stderr,"\n");
  764.      needCRLF = FALSE;
  765.     }
  766.  
  767. /****************************************************************************
  768. *                                  strip_out
  769. * Inputs:
  770. *       char * file: file name to strip out (note that it must be in the
  771. *            same form as the one in the file)
  772. *
  773. * Result: boolean
  774. *    true if operation was successful
  775. *    false if it failed for some reason
  776. * Effect: 
  777. *       renames "tags" to "otags", copies it back to "tags" stripping out
  778. *    entries for the current file
  779. ****************************************************************************/
  780.  
  781. int strip_out(file)
  782.     char * file;
  783.     {
  784.      FILE * old;
  785.      FILE * new;
  786.  
  787.      fprintf(stderr,"stripping out old entries...");
  788.      fflush(stderr);
  789.      needCRLF = TRUE;
  790.  
  791.      remove("otags");
  792.      if(rename("tags","otags"))
  793.         { /* failed */
  794.      fprintf(stderr,"Unable to rename 'tags' to 'otags', update cannot be done\n");
  795.      return FALSE;
  796.     } /* failed */
  797.      
  798.      old = fopen("otags","r");
  799.      if(old==NULL)
  800.         { /* failed */
  801.      fprintf(stderr,"failed to open 'otags' for copy, update cannot be done\n");
  802.      return FALSE;
  803.     } /* failed */
  804.  
  805.      new = fopen(outfile,"w");
  806.  
  807.      if(new==NULL)
  808.         { /* failed again */
  809.      fprintf(stderr,"failed to open '%s' for copy, update cannot be done\n",
  810.              outfile);
  811.      close(old);
  812.      return FALSE;
  813.     } /* failed again */
  814.  
  815.      while(TRUE)
  816.         { /* copy lines */
  817.      char line[256];
  818.      char * p1;
  819.      char * p2;
  820.  
  821.      fgets(line,256,old);
  822.  
  823.      if(feof(old)) break;
  824.  
  825.      p1 = stpchr(line,'\t');    /* find initial tab char */
  826.  
  827.      if(p1 != NULL)
  828.         { /* ok */
  829.          p1++;
  830.          p2 = stpchr(p1,'\t');    /* find next tab char */
  831.  
  832.          if(p2 != NULL)
  833.             { /* found it! */
  834.          if(strncmp(p1,file,p2-p1) == 0)
  835.             { /* strip it! */
  836.              continue;
  837.             } /* strip it! */
  838.         } /* found it! */
  839.         } /* ok */
  840.      fputs(line,new);
  841.     } /* copy lines */
  842.      
  843.      fclose(old);
  844.      fclose(new);
  845.  
  846.      printf("\n");
  847.  
  848.      return TRUE;
  849.     }
  850.  
  851. /****************************************************************************
  852. *                                  each_file
  853. * Inputs:
  854. *       char * file: File name to process
  855. * Result: void
  856. *       
  857. * Effect: 
  858. *       Processes the file.
  859. ****************************************************************************/
  860.  
  861. void each_file(file)
  862.     char * file;
  863.     {
  864.       find_entries(file);
  865.       if (eflag)
  866.     {
  867.       fprintf (outf, "\f\n%s,%d\n",
  868.            file, total_size_of_entries (head));
  869.       put_entries (head);
  870.       free_tree (head);
  871.       head = NULL;
  872.     }
  873.  
  874.      if (eflag)
  875.     {
  876.      fclose (outf);
  877.     }
  878.  
  879.      if (xflag)
  880.     {
  881.      put_entries(head);
  882.     }
  883.  
  884.      if (uflag)
  885.     {
  886.      /* 
  887.         The purpose of the following sequence is to copy everything
  888.         from the original output file to a file of the same name,
  889.         eliminating all of the information of the current file
  890.         under consideration.
  891.  
  892.         In Unix, this is done by using fgrep to remove all but the
  893.         strings of the current file name.
  894.  
  895.         In MS-DOS, we have to be much, much sneakier, but I'm not
  896.         going to write that code right now.
  897.      */
  898.      /*****************************************************************
  899.         sprintf(cmd,
  900.           "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
  901.           outfile, file, outfile);
  902.         system(cmd);
  903.          ****************************************************************/
  904.      if(strip_out(file))
  905.         aflag++;
  906.      else
  907.         return;
  908.     }
  909.  
  910.  /* If we are in update mode, we open for append access (having just
  911.     eliminated the data for our current file in the section above
  912.     if necessary); otherwise we overwrite the output file
  913.     If we have already processed one file, we also have incremented aflag,
  914.     so we will be in update mode also.
  915.  */
  916.  
  917.     outf = fopen(outfile, aflag ? "a" : "w");
  918.     if (outf == NULL)
  919.        { /* output open failed */
  920.     perror(outfile);
  921.     exit(1);
  922.        } /* output open failed */
  923.  
  924.     put_entries(head);
  925.     fclose(outf);
  926.  
  927.     }
  928.  
  929. /****************************************************************************
  930. *                                    init
  931. * Effect: 
  932. *      This routine sets up the boolean psuedo-functions which work
  933. *      by seting boolean flags dependent upon the corresponding character
  934. *      Every char which is NOT in that string is not a white char.  
  935. *      Therefore, all of the array "_wht" is set to FALSE, and then the 
  936. *      elements subscripted by the chars in "white" are set to TRUE.  Thus 
  937. *      "_wht" of a char is TRUE if it is the string "white", else FALSE.
  938. ****************************************************************************/
  939.  
  940. void init()
  941. {
  942.  
  943.   reg char *sp;
  944.   reg int i;
  945.  
  946.   for (i = 0; i < 0177; i++)
  947.     {
  948.       _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
  949.       _gd[i] = TRUE;
  950.     }
  951.   for (sp = white; *sp; sp++)
  952.     _wht[*sp] = TRUE;
  953.   for (sp = endtk; *sp; sp++)
  954.     _etk[*sp] = TRUE;
  955.   for (sp = intk; *sp; sp++)
  956.     _itk[*sp] = TRUE;
  957.   for (sp = begtk; *sp; sp++)
  958.     _btk[*sp] = TRUE;
  959.   for (sp = notgd; *sp; sp++)
  960.     _gd[*sp] = FALSE;
  961.   _wht[0] = _wht['\n'];
  962.   _etk[0] = _etk['\n'];
  963.   _btk[0] = _btk['\n'];
  964.   _itk[0] = _itk['\n'];
  965.   _gd[0] = _gd['\n'];
  966. }
  967.  
  968. /****************************************************************************
  969. *                                    tagit
  970. * Inputs:
  971. *       char * file: Name of file
  972. * Result: void
  973. *
  974. * Effect: 
  975. *       Writes a .tag file of the same name as the input file; this is
  976. *    for use with makefiles so that tags can be automatically updated
  977. *    (note that recompilation should not affect tag regeneration, since
  978. *    it may be that the source is unchanged but a dependent file is
  979. *    changed)
  980. ****************************************************************************/
  981.  
  982. void tagit(file)
  983.     char * file;
  984.     {
  985.      char tagname[80];
  986.      char * p;
  987.      FILE * tagfile;
  988.  
  989.      strcpy(tagname,file);
  990.  
  991.      p = rindex(tagname,'.');
  992.      if(p!=NULL) *p = '\0';
  993.      strcat(tagname,".tag");
  994.      tagfile = fopen(tagname,"w");
  995.      if(tagfile == NULL)
  996.         { /* failed */
  997.      fprintf(stderr,"Failed to create \"%s\", auto-retagging may file in your makefile\n",tagfile);
  998.      return;
  999.     } /* failed */
  1000.      fclose(tagfile);
  1001.      return;
  1002.      
  1003.     }
  1004.  
  1005. /****************************************************************************
  1006. *                                find_entries
  1007. * Inputs:
  1008. *       char * file
  1009. * Result: void
  1010. *
  1011. * Effect: 
  1012. *      This routine opens the specified file and calls the function
  1013. *      which finds the function and type definitions.
  1014. ****************************************************************************/
  1015.  
  1016. void find_entries (file)
  1017.      char *file;
  1018. {
  1019.   char *cp;
  1020.  
  1021.   if ((inf=fopen(file,"r")) == NULL)
  1022.     {
  1023.       perror(file);
  1024.       return;
  1025.     }
  1026.   curfile = savestr(file);
  1027.   cp = rindex(file, '.');
  1028.  
  1029.   /* .l or .el or .lisp implies lisp source code */
  1030.   /* LISP MODE FILE */
  1031.  
  1032.   if (cp && (!strcmp (cp + 1, "l") || !strcmp (cp + 1, "el")
  1033.          || !strcmp (cp + 1, "lisp")))
  1034.     {
  1035.       L_funcs(inf);
  1036.       fclose(inf);
  1037.       if(mflag) tagit(file);
  1038.       return;
  1039.     }
  1040.  
  1041.  /* If a .MSS file, try Scribe mode */
  1042.  
  1043.  if (cp && (strcmp(cp+1,"mss") == 0))
  1044.     { /* scribe */
  1045.      S_labels(inf);
  1046.      fclose(inf);
  1047.      if(mflag) tagit(file);
  1048.      return;
  1049.     } /* scribe */
  1050.  
  1051.  /* If a .TEX file, try TeX mode */
  1052.  
  1053.  if (cp && (strcmp(cp+1,"tex") == 0))
  1054.     { /* TeX */
  1055.      T_labels(inf);
  1056.      fclose(inf);
  1057.      if(mflag) tagit(file);
  1058.      return;
  1059.     } /* TeX */
  1060.  
  1061.  
  1062.   /* if not a .c or .h or .y file, try fortran */
  1063.   if (cp && (cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
  1064.       && cp[2] == '\0')
  1065.     {
  1066.       if (PF_funcs(inf) != 0)
  1067.     {
  1068.       fclose(inf);
  1069.       if(mflag) tagit(file);
  1070.       return;
  1071.     }
  1072.       rewind(inf);    /* no fortran tags found, try C */
  1073.     }
  1074.   C_entries();
  1075.   fclose(inf);
  1076.   if(mflag) tagit(file);
  1077. }
  1078.  
  1079. /****************************************************************************
  1080. *                                   pfnote
  1081. * Inputs:
  1082. *       char * name:
  1083. *    logical f:
  1084. *    char * linestart:  pointer to start of line
  1085. *    int linelen: length of line in characters
  1086. *    int lno: Number of line in input file
  1087. *    long cno: Character number in input file
  1088. * Result: void
  1089. *       
  1090. * Effect: 
  1091. *       Record a tag on the current line.
  1092. *      'name' is the tag name,
  1093. *      'f' is nonzero to use a pattern, zero to use line number instead.
  1094. ****************************************************************************/
  1095.  
  1096. void pfnote (name, f, linestart, linelen, lno, cno)
  1097.      char *name;
  1098.      logical f;            /* f == TRUE when function */
  1099.      char *linestart;
  1100.      int linelen;
  1101.      int lno;
  1102.      long cno;
  1103. {
  1104.   reg char *fp;
  1105.   reg NODE *np;
  1106.   char *altname;
  1107.   char tem[51];
  1108.  
  1109. #if debug
  1110.   printf("pfnote(\"%s\",%d,\"%s\",%d,%d,%ld)\n",
  1111.               name,f,linestart,linelen,lno,cno);
  1112. #endif
  1113.   if ((np = (NODE *) malloc(sizeof (NODE))) == NULL)
  1114.     {
  1115.       fprintf(stderr, "ctags: too many entries to sort\n");
  1116.       put_entries(head);
  1117.       free_tree(head);
  1118.       head = NULL;
  1119.       np = (NODE *) xmalloc(sizeof (NODE));
  1120.     }
  1121.   /* Change name "main" to M<thisfilename>. */
  1122.   if (!eflag && !xflag && !strcmp(name, "main"))
  1123.     {
  1124.       fp = rindex(curfile, '\\');
  1125.       if (fp == 0)
  1126.     fp = curfile;
  1127.       else
  1128.     fp++;
  1129.       altname = concat ("M", fp, "");
  1130.       fp = rindex(altname, '.');
  1131.       if (fp && fp[2] == 0)
  1132.     *fp = 0;
  1133.       name = altname;
  1134.     }
  1135.   np->entry_name = savestr(name);
  1136.   np->file = curfile;
  1137.   np->f = f;
  1138.   np->lno = lno;
  1139.   np->cno = cno;
  1140.   np->left = np->right = 0;
  1141.   if (eflag)
  1142.     {
  1143.       linestart[linelen] = 0;
  1144.     }
  1145.   else if (xflag == 0)
  1146.     {
  1147.       sprintf (tem, strlen (linestart) < 40 ? "%s$" : "%.40s", linestart);
  1148.       linestart = tem;
  1149.     }
  1150.   np->pat = savestr (linestart);
  1151.   if (head == NULL)
  1152.     head = np;
  1153.   else
  1154.     add_node(np, head);
  1155. }
  1156.  
  1157. /****************************************************************************
  1158. *                                  free_tree
  1159. * Inputs:
  1160. *       NODE * node: Root of tree to free
  1161. * Result: void
  1162. *
  1163. * Effect: 
  1164. *       Releases all the storage in the tree
  1165. ****************************************************************************/
  1166.  
  1167. void free_tree(node)
  1168.      NODE *node;
  1169. {
  1170.   while (node)
  1171.     {
  1172.       free_tree(node->right);
  1173.       free(node);
  1174.       node = node->left;
  1175.     }
  1176. }
  1177.  
  1178. /****************************************************************************
  1179. *                                  add_node
  1180. * Inputs:
  1181. *       NODE * node: Node to add
  1182. *    NODE * cur_node: Node at which we are currently sitting
  1183. * Result: void
  1184. *       
  1185. * Effect: 
  1186. *       Adds the node into the tree as a descendent of cur_node
  1187. ****************************************************************************/
  1188.  
  1189. void add_node(node, cur_node)
  1190.      NODE *node,*cur_node;
  1191. {
  1192.   register int dif;
  1193.  
  1194.   dif = strcmp(node->entry_name, cur_node->entry_name);
  1195.  
  1196.   /* If this tag name matches an existing one, then
  1197.      unless -e was given, do not add the node, but maybe print a warning */
  1198.   if (!eflag && !dif)
  1199.     {
  1200.       if (node->file == cur_node->file)
  1201.     {
  1202.       if (!wflag)
  1203.         {
  1204.          checkCRLF();
  1205.           fprintf(stderr,"%s %d Warning: Duplicate entry \"%s\"\n",
  1206.               node->file,lineno,node->entry_name);
  1207.           fprintf(stderr,"Second entry ignored\n");
  1208.         }
  1209.       return;
  1210.     }
  1211.       if (!cur_node->been_warned)
  1212.     if (!wflag)
  1213.        { /* print warning */
  1214.         checkCRLF();
  1215.  
  1216.         fprintf(stderr,"Duplicate entry in files %s and %s: %s (Warning only)\n",
  1217.           node->file, cur_node->file, node->entry_name);
  1218.        } /* print warning */
  1219.  
  1220.       cur_node->been_warned = TRUE;
  1221.       return;
  1222.     } 
  1223.  
  1224.   /* Actually add the node */
  1225.   if (dif < 0) 
  1226.     {
  1227.       if (cur_node->left != NULL)
  1228.     add_node(node,cur_node->left);
  1229.       else
  1230.     cur_node->left = node;
  1231.       return;
  1232.     }
  1233.   if (cur_node->right != NULL)
  1234.     add_node(node,cur_node->right);
  1235.   else
  1236.     cur_node->right = node;
  1237. }
  1238.  
  1239. /****************************************************************************
  1240. *                                 put_entries
  1241. * Inputs:
  1242. *       NODE * node:
  1243. * Result: void
  1244. *       
  1245. * Effect: 
  1246. *       2
  1247. ****************************************************************************/
  1248.  
  1249. void put_entries(node)
  1250.      reg NODE *node;
  1251. {
  1252.   reg char *sp;
  1253.  
  1254.   if (node == NULL)
  1255.     return;
  1256.  
  1257.   /* Output subentries that precede this one */
  1258.   put_entries (node->left);
  1259.  
  1260.   /* Output this entry */
  1261.  
  1262.   if (eflag)
  1263.     {
  1264.       fprintf (outf, "%s%c%d,%d\n",
  1265.            node->pat, 0177, node->lno, node->cno);
  1266.     }
  1267.   else if (!xflag)
  1268.     {
  1269.       fprintf (outf, "%s\t%s\t",
  1270.            node->entry_name, node->file);
  1271.  
  1272.       if (node->f)
  1273.     {        /* a function */
  1274.       putc (searchar, outf);
  1275.       putc ('^', outf);
  1276.  
  1277.       for (sp = node->pat; *sp; sp++)
  1278.         {
  1279.           if ( /* Epsilon re_search chars quoted */
  1280.             *sp == searchar 
  1281.          || *sp == '*'
  1282.          || *sp == '%'
  1283.          || *sp == '^'
  1284.          || *sp == '.'
  1285.          || *sp == '['
  1286.          || *sp == ']'
  1287.          || *sp == '('
  1288.          || *sp == ')'
  1289.          || *sp == '+'
  1290.          || *sp == '|'
  1291.          || *sp == '!')
  1292.         putc ('%', outf);    /* put out quoting char */
  1293.           putc (*sp, outf);
  1294.         }
  1295.       putc (searchar, outf);
  1296.     }
  1297.       else
  1298.     {        /* a typedef; text pattern inadequate */
  1299.       fprintf (outf, "%d", node->lno);
  1300.     }
  1301.       putc ('\n', outf);
  1302.     }
  1303.   else if (vflag)
  1304.     fprintf (stdout, "%s %s %d\n",
  1305.          node->entry_name, node->file, (node->lno+63)/64);
  1306.   else
  1307.     fprintf (stdout, "%-16s%4d %-16s %s\n",
  1308.          node->entry_name, node->lno, node->file, node->pat);
  1309.  
  1310.   /* Output subentries that follow this one */
  1311.   put_entries (node->right);
  1312. }
  1313.  
  1314.  
  1315. /****************************************************************************
  1316. *                            total_size_of_entries
  1317. * Inputs:
  1318. *       NODE * node: root of subtree
  1319. * Result: int
  1320. *     Return total number of characters that put_entries will output for
  1321. *     the nodes in the subtree of the specified node.
  1322. *     Works only if eflag is set, but called only in that case.  
  1323. ****************************************************************************/
  1324.  
  1325.  
  1326. int total_size_of_entries(node)
  1327.      reg NODE *node;
  1328. {
  1329.   reg int total = 0;
  1330.   reg long num;
  1331.  
  1332.   if (node == NULL)
  1333.     return 0;
  1334.  
  1335.   /* Count subentries that precede this one */
  1336.   total = total_size_of_entries (node->left);
  1337.  
  1338.   /* Count subentries that follow this one */
  1339.   total += total_size_of_entries (node->right);
  1340.  
  1341.   /* Count this entry */
  1342.  
  1343.   total += strlen (node->pat) + 3;
  1344.  
  1345.   num = node->lno;
  1346.   while (num)
  1347.     {
  1348.       total++;
  1349.       num /= 10;
  1350.     }
  1351.  
  1352.   num = node->cno;
  1353.   if (!num) total++;
  1354.   while (num)
  1355.     {
  1356.       total++;
  1357.       num /= 10;
  1358.     }
  1359.   return total;
  1360. }
  1361.  
  1362. /****************************************************************************
  1363. *                               CNL_SAVE_NUMBER
  1364. * Effect: 
  1365. *       2
  1366. ****************************************************************************/
  1367.  
  1368. #define CNL_SAVE_NUMBER \
  1369. { \
  1370.   linecharno = charno; lineno++; \
  1371.   charno = readline (&lb, inf); \
  1372.   lp = lb.buffer; \
  1373. }
  1374.  
  1375. /****************************************************************************
  1376. *                                     CNL
  1377. * Effect: 
  1378. *       2
  1379. ****************************************************************************/
  1380.  
  1381.  
  1382. #define CNL \
  1383. { \
  1384.   CNL_SAVE_NUMBER; \
  1385.   number = 0; \
  1386. }
  1387.  
  1388. /****************************************************************************
  1389. *                                  C_entries
  1390. * Result: 2
  1391. *       2
  1392. * Effect: 
  1393. *      This routine finds functions and typedefs in C syntax and adds them
  1394. *      to the list.
  1395. ****************************************************************************/
  1396.  
  1397. void C_entries ()
  1398. {
  1399.   reg int c;
  1400.   reg char *token, *tp, *lp;
  1401.   logical incomm, inquote, inchar, midtoken;
  1402.   int level;
  1403.   char tok[BUFSIZ];
  1404.  
  1405.   token = NULL;        /* avoid uninitialized var errors, even though
  1406.                  flow of control makes it impossible*/
  1407.   tp = NULL;        /* likewise */
  1408.  
  1409.   fprintf(stderr," C mode ");
  1410.   fflush(stderr);
  1411.   needCRLF = TRUE;
  1412.  
  1413.   lineno = 0;
  1414.   charno = 0;
  1415.   lp = lb.buffer;
  1416.   *lp = 0;
  1417.  
  1418.   number = 0;
  1419.   gotone = midtoken = inquote = inchar = incomm = FALSE;
  1420.   level = 0;
  1421.  
  1422.   while (!feof (inf))
  1423.     {
  1424.       c = *lp++;
  1425.       if (c == 0)
  1426.     {
  1427.       CNL;
  1428.       gotone = FALSE;
  1429.     }
  1430.       if (c == '\\')
  1431.     {
  1432.       c = *lp++;
  1433.       if (c == 0)
  1434.         CNL_SAVE_NUMBER;
  1435.       c = ' ';
  1436.     } 
  1437.       else if (incomm)
  1438.     {
  1439.       if (c == '*')
  1440.         {
  1441.           while ((c = *lp++) == '*')
  1442.         continue;
  1443.           if (c == 0)
  1444.         CNL;
  1445.           if (c == '/')
  1446.         incomm = FALSE;
  1447.         }
  1448.     }
  1449.       else if (inquote)
  1450.     {
  1451.       /*
  1452.       * Too dumb to know about \" not being magic, but
  1453.       * they usually occur in pairs anyway.
  1454.       */
  1455.       if (c == '"')
  1456.         inquote = FALSE;
  1457.       continue;
  1458.     }
  1459.       else if (inchar)
  1460.     {
  1461.       if (c == '\'')
  1462.         inchar = FALSE;
  1463.       continue;
  1464.     }
  1465.       else switch (c)
  1466.          { /* decode char */      
  1467.     case '"':
  1468.       inquote = TRUE;
  1469.       continue;
  1470.     case '\'':
  1471.       inchar = TRUE;
  1472.       continue;
  1473.     case '/':
  1474.       if (*lp == '*')
  1475.         {
  1476.           lp++;
  1477.           incomm = TRUE;
  1478.         }
  1479.       continue;
  1480.     case '#':
  1481.       if (lp == lb.buffer + 1)
  1482.         number = 1;
  1483.       continue;
  1484.     case '{':
  1485.       if (tydef == begin)
  1486.         {
  1487.           tydef=middle;
  1488.         }
  1489.       level++;
  1490.       continue;
  1491.     case '}':
  1492.       if (lp == lb.buffer + 1)
  1493.         level = 0;    /* reset */
  1494.       else
  1495.         level--;
  1496.       if (!level && tydef==middle)
  1497.         {
  1498.           tydef=end;
  1499.         }
  1500.       continue;
  1501.      } /* decode char */
  1502.  
  1503.       if (!level && !inquote && !incomm && gotone == FALSE)
  1504.     {
  1505.       if (midtoken)
  1506.         {
  1507.           if (endtoken(c))
  1508.         {
  1509.           int f;
  1510.           char *buf = lb.buffer;
  1511.           int endpos = lp - lb.buffer;
  1512.           char *lp1 = lp;
  1513.           int line = lineno;
  1514.           long linestart = linecharno;
  1515.           int tem = consider_token (&lp1, token, &f);
  1516.  
  1517.           lp = lp1;
  1518.           if (tem)
  1519.             {
  1520.               if (linestart != linecharno)
  1521.             {
  1522.               getline (linestart);
  1523.               strncpy (tok, token + (lb1.buffer - buf),
  1524.                    tp-token+1);
  1525.               tok[tp-token+1] = 0;
  1526.               pfnote(tok, f, lb1.buffer, endpos - 1, line, linestart);
  1527.             }
  1528.               else
  1529.             {
  1530.               strncpy (tok, token, tp-token+1);
  1531.               tok[tp-token+1] = 0;
  1532.               pfnote(tok, f, lb.buffer, endpos - 1, line, linestart);
  1533.             }
  1534.               gotone = f;    /* function */
  1535.             }
  1536.           midtoken = FALSE;
  1537.           token = lp - 1;
  1538.         }
  1539.           else if (intoken(c))
  1540.         tp++;
  1541.         }
  1542.       else if (begtoken(c))
  1543.         {
  1544.           token = tp = lp - 1;
  1545.           midtoken = TRUE;
  1546.         }
  1547.     }
  1548.       if (c == ';'  &&  tydef==end)    /* clean with typedefs */
  1549.     tydef=none;
  1550.     }
  1551. }
  1552.  
  1553. /****************************************************************************
  1554. *                               consider_token
  1555. * Inputs:
  1556. *       char ** lpp: Pointer to line position pointer
  1557. *    char * token: Pointer to token buffer
  1558. *    int * f:
  1559. * Result: boolean
  1560. *       2
  1561. * Effect: 
  1562. *      This routine  checks to see if the current token is
  1563. *      at the start of a function, or corresponds to a typedef
  1564. *      It updates the input line * 'lpp' so that the '(' will be
  1565. *      in it when it returns.
  1566. ****************************************************************************/
  1567.  
  1568. consider_token (lpp, token, f)
  1569.      char **lpp, *token;
  1570.      int *f;
  1571. {
  1572.   reg char *lp = *lpp;
  1573.   reg char c;
  1574.   static logical next_token_is_func;
  1575.   logical firsttok;    /* T if have seen first token in ()'s */
  1576.   int bad, win;
  1577.  
  1578. #if debug
  1579.     printf("consider_token(0x%x:\"%s\",0x%x,0x%x)\n",lpp,*lpp,token,f);
  1580. #endif
  1581.   *f = 1;            /* a function */
  1582.   c = lp[-1];
  1583.   bad = FALSE;
  1584.   if (!number)
  1585.     {        /* space is not allowed in macro defs    */
  1586.       while (iswhite(c))
  1587.     {
  1588.       c = *lp++;
  1589.       if (c == 0)
  1590.         {
  1591.           if (feof (inf))
  1592.         break;
  1593.           CNL;
  1594.         }
  1595.     }
  1596.       /* the following tries to make it so that a #define a b(c)    */
  1597.       /* doesn't count as a define of b.                */
  1598.     }
  1599.   else
  1600.     {
  1601.       number++;
  1602.       if (number >= 4  || (number==2 && strncmp (token, "define", 6)))
  1603.     {
  1604.       gotone = TRUE;
  1605.     badone:
  1606.       bad = TRUE;
  1607.       goto ret;
  1608.     }
  1609.     }
  1610.   /* check for the typedef cases        */
  1611.   if (tflag && !strncmp(token, "typedef", 7))
  1612.     {
  1613.       tydef=begin;
  1614.       goto badone;
  1615.     }
  1616.   if (tydef==begin && (!strncmp(token, "struct", 6) ||
  1617.                !strncmp(token, "union", 5) || !strncmp(token, "enum", 4)))
  1618.   {
  1619.     goto badone;
  1620.   }
  1621.   if (tydef==begin)
  1622.     {
  1623.       tydef=end;
  1624.       goto badone;
  1625.     }
  1626.   if (tydef==end)
  1627.     {
  1628.       *f = 0;
  1629.       win = 1;
  1630.       goto ret;
  1631.     }
  1632.   /* Detect GNUmacs's function-defining macros. */
  1633.   if (!number && !strncmp (token, "DEF", 3))
  1634.      
  1635.     {
  1636.       next_token_is_func = 1;
  1637.       goto badone;
  1638.     }
  1639.   if (next_token_is_func)
  1640.     {
  1641.       next_token_is_func = 0;
  1642.       win = 1;
  1643.       goto ret;
  1644.     }
  1645.   if (c != '(')
  1646.     goto badone;
  1647.   firsttok = FALSE;
  1648.   while ((c = *lp++) != ')')
  1649.     {
  1650.       if (c == 0)
  1651.     {
  1652.       if (feof (inf))
  1653.         break;
  1654.       CNL;
  1655.     }
  1656.       /*
  1657.     * This line used to confuse ctags:
  1658.     *    int    (*oldhup)();
  1659.     * This fixes it. A nonwhite char before the first
  1660.     * token, other than a / (in case of a comment in there)
  1661.     * makes this not a declaration.
  1662.     */
  1663.       if (begtoken(c) || c=='/') firsttok++;
  1664.       else if (!iswhite(c) && !firsttok) goto badone;
  1665.     }
  1666.   while (iswhite (c = *lp++))
  1667.     {
  1668.       if (!c)
  1669.     CNL;
  1670.     }
  1671.   win = isgood (c);
  1672. ret:
  1673.   *lpp = lp - 1;
  1674.   return !bad && win;
  1675. }
  1676.  
  1677. /****************************************************************************
  1678. *                                   getline
  1679. * Inputs:
  1680. *       long atchar: character at which to start scan
  1681. * Result: void
  1682. *
  1683. * Effect: 
  1684. *       Reads an input line starting at 'atchar' and proceeding thru the
  1685. *    next linefeed character
  1686. ****************************************************************************/
  1687.  
  1688. void getline (atchar)
  1689.      long atchar;
  1690. {
  1691.   long saveftell = ftell (inf);
  1692.  
  1693. #if debug
  1694.   printf("getline(%ld): saveftell = %ld",atchar,saveftell);
  1695. #endif
  1696.   fseek (inf, atchar, 0);
  1697.   readline (&lb1, inf);
  1698. #if debug
  1699.    printf(" => \"%s\"\n",lb.buffer);
  1700. #endif
  1701.   fseek (inf, saveftell, 0);
  1702. }
  1703.  
  1704. /* Fortran parsing */
  1705.  
  1706. char    *dbp;
  1707. int    pfcnt;
  1708.  
  1709. PF_funcs(fi)
  1710.      FILE *fi;
  1711. {
  1712.   lineno = 0;
  1713.   charno = 0;
  1714.   pfcnt = 0;
  1715.  
  1716.   while (!feof (fi))
  1717.     {
  1718.       lineno++;
  1719.       linecharno = charno;
  1720.       charno = readline (&lb, fi);
  1721.       dbp = lb.buffer;
  1722.       if (*dbp == '%') dbp++ ;    /* Ratfor escape to fortran */
  1723.       while (isspace(*dbp))
  1724.     dbp++;
  1725.       if (*dbp == 0)
  1726.     continue;
  1727.       switch (*dbp |' ')
  1728.     {
  1729.     case 'i':
  1730.       if (tail("integer"))
  1731.         takeprec();
  1732.       break;
  1733.     case 'r':
  1734.       if (tail("real"))
  1735.         takeprec();
  1736.       break;
  1737.     case 'l':
  1738.       if (tail("logical"))
  1739.         takeprec();
  1740.       break;
  1741.     case 'c':
  1742.       if (tail("complex") || tail("character"))
  1743.         takeprec();
  1744.       break;
  1745.     case 'd':
  1746.       if (tail("double"))
  1747.         {
  1748.           while (isspace(*dbp))
  1749.         dbp++;
  1750.           if (*dbp == 0)
  1751.         continue;
  1752.           if (tail("precision"))
  1753.         break;
  1754.           continue;
  1755.         }
  1756.       break;
  1757.     }
  1758.       while (isspace(*dbp))
  1759.     dbp++;
  1760.       if (*dbp == 0)
  1761.     continue;
  1762.       switch (*dbp|' ')
  1763.     {
  1764.     case 'f':
  1765.       if (tail("function"))
  1766.         getit();
  1767.       continue;
  1768.     case 's':
  1769.       if (tail("subroutine"))
  1770.         getit();
  1771.       continue;
  1772.     case 'p':
  1773.       if (tail("program"))
  1774.         {
  1775.           getit();
  1776.           continue;
  1777.         }
  1778.       if (tail("procedure"))
  1779.         getit();
  1780.       continue;
  1781.     }
  1782.     }
  1783.   return (pfcnt);
  1784. }
  1785.  
  1786. int tail(cp)
  1787.      char *cp;
  1788. {
  1789.   register int len = 0;
  1790.  
  1791.   while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
  1792.     cp++, len++;
  1793.   if (*cp == 0)
  1794.     {
  1795.       dbp += len;
  1796.       return (1);
  1797.     }
  1798.   return (0);
  1799. }
  1800.  
  1801. void takeprec()
  1802. {
  1803.   while (isspace(*dbp))
  1804.     dbp++;
  1805.   if (*dbp != '*')
  1806.     return;
  1807.   dbp++;
  1808.   while (isspace(*dbp))
  1809.     dbp++;
  1810.   if (!isdigit(*dbp))
  1811.     {
  1812.       --dbp;        /* force failure */
  1813.       return;
  1814.     }
  1815.   do
  1816.     dbp++;
  1817.   while (isdigit(*dbp));
  1818. }
  1819.  
  1820. /****************************************************************************
  1821. *                                    getit
  1822. * Result: void
  1823. *       
  1824. * Effect: 
  1825. *       2
  1826. ****************************************************************************/
  1827.  
  1828. void getit()
  1829. {
  1830.   register char *cp;
  1831.   char c;
  1832.   char nambuf[BUFSIZ];
  1833.  
  1834.   while (isspace(*dbp))
  1835.     dbp++;
  1836.   if (*dbp == 0 || !isalpha(*dbp))
  1837.     return;
  1838.   for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
  1839.     continue;
  1840.   c = cp[0];
  1841.   cp[0] = 0;
  1842.   strcpy(nambuf, dbp);
  1843.   cp[0] = c;
  1844.   pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
  1845.   pfcnt++;
  1846. }
  1847.  
  1848. /****************************************************************************
  1849. *                                   get_bkt
  1850. * Inputs:
  1851. *       char * bp: Bracket pointer to start of bracketed string (may have
  1852. *            leading whitespace)
  1853. * Result: char *
  1854. *       Bracket pointer to ending bracket
  1855. ****************************************************************************/
  1856.  
  1857. char * get_bkt(bp)
  1858.     char * bp;
  1859.     {
  1860.      char * ebp;
  1861.      char ebkt;
  1862.  
  1863.      /* skip initial whitespace */
  1864.  
  1865.      while(*bp == ' ') bp++;
  1866.  
  1867.      switch(*bp)
  1868.         { /* find match */
  1869. #define mbkt(l,r)  case l: ebkt = r; break
  1870.          mbkt('[',']');
  1871.         mbkt('(',')');
  1872.         mbkt('<','>');
  1873.         mbkt('{','}');
  1874.         mbkt('\"','\"');
  1875.         default: ebkt = NULL;
  1876.     } /* find match */
  1877.  
  1878.      if(!ebkt) return NULL;
  1879.  
  1880.      ebp = stpchr(++bp,ebkt);
  1881.      
  1882.      return ebp;
  1883.      
  1884.     }
  1885.  
  1886. /****************************************************************************
  1887. *                                  S_labels
  1888. * Inputs:
  1889. *       FILE * fi: Input file
  1890. * Effect: 
  1891. *       Scans for @tag and @label directives
  1892. ****************************************************************************/
  1893.  
  1894. void S_labels(fi)
  1895.     FILE * fi;
  1896.     {
  1897.      char nambuf[BUFSIZ];
  1898.      char * ebp;
  1899.      char * dbp;
  1900.      char * obp;
  1901.      char * cp;
  1902.  
  1903.      fprintf(stderr," Scribe mode ");
  1904.      fflush(stderr);
  1905.      needCRLF = TRUE;
  1906.  
  1907.      lineno = 0;
  1908.      charno = 0;
  1909.      pfcnt = 0;
  1910.  
  1911.      while (!feof(fi))
  1912.         { /* read lines */
  1913.  
  1914.      lineno++;
  1915.      linecharno = charno;
  1916.      charno = readline(&lb,fi);
  1917.      dbp = lb.buffer;
  1918.      while( dbp = stpchr(dbp,'@'))
  1919.         { /* some Scribe command... */
  1920.          ebp = NULL;
  1921.          if(strncmp(dbp,"@label",6) == 0)
  1922.             { /* @label */
  1923.          obp = &dbp[6];
  1924.          while(*obp == ' ') obp++;
  1925.          ebp = get_bkt(obp);
  1926.          if(ebp != NULL)
  1927.             { /* parse label */
  1928.              char c;
  1929.               /* @label < name > 
  1930.                * ^dbp          ^ebp
  1931.                *        ^obp
  1932.               */
  1933.               
  1934.               while (*++obp == ' ') ;
  1935.  
  1936.               c = *ebp;
  1937.               *ebp = '\0';
  1938.               strcpy(nambuf,obp);
  1939.               *ebp = c;
  1940.  
  1941.               /* trim trailing whitespace */
  1942.  
  1943.               while(cp = rindex(nambuf,' ')) *cp = '\0';
  1944.  
  1945.               pfnote(nambuf, TRUE, dbp, ebp - dbp + 1, lineno, linecharno);
  1946.               pfcnt++;
  1947.             } /* parse label */
  1948.         } /* @label */
  1949.          else
  1950.          if(strncmp(dbp,"@tag",4) == 0)
  1951.             { /* @tag */
  1952.          obp = &dbp[4];
  1953.          /* @tag < name > 
  1954.           * ^dbp
  1955.               *     ^obp
  1956.          */
  1957.  
  1958.          while(*obp == ' ') obp++;
  1959.          /* @tag < name > 
  1960.           * ^dbp
  1961.               *      ^obp
  1962.          */
  1963.  
  1964.          ebp = get_bkt(obp);
  1965.          if(ebp != NULL)
  1966.             { /* parse tag */
  1967.              char c;
  1968.               /* @tag < name > 
  1969.                * ^dbp        ^ebp
  1970.                *      ^obp
  1971.               */
  1972.               
  1973.               while( *++obp == ' ') ;
  1974.  
  1975.               /* @tag < name > 
  1976.                * ^dbp        ^ebp
  1977.                *        ^obp
  1978.               */
  1979.               
  1980.               c = *ebp;
  1981.               *ebp = '\0';
  1982.               strcpy(nambuf,obp);
  1983.               *ebp = c;
  1984.  
  1985.               /* It may be of the form
  1986.                  @tag < name = value >
  1987.              so nambuf contains
  1988.              name = value
  1989.               */
  1990.  
  1991.               if(cp = stpchr(nambuf,'='))
  1992.                  { /* equality */
  1993.               *cp = '\0';
  1994.              } /* equality */
  1995.  
  1996.               /* trim trailing whitespace */
  1997.  
  1998.               while(cp = rindex(nambuf,' ')) *cp = '\0';
  1999.  
  2000.               pfnote(nambuf, TRUE, dbp, ebp - dbp + 1, lineno, linecharno);
  2001.               pfcnt++;
  2002.             } /* parse tag */
  2003.         } /* @tag */
  2004.          
  2005.          dbp++;
  2006.         } /* some Scribe command... */
  2007.     } /* read lines */
  2008.     }
  2009.  
  2010. /****************************************************************************
  2011. *                                  T_labels
  2012. * Inputs:
  2013. *       FILE * fi: Input file
  2014. * Effect: 
  2015. *       Scans for \label directives
  2016. ****************************************************************************/
  2017.  
  2018. void T_labels(fi)
  2019.     FILE * fi;
  2020.     {
  2021.      char nambuf[BUFSIZ];
  2022.      char * ebp;
  2023.      char * dbp;
  2024.      char * obp;
  2025.      char * cp;
  2026.  
  2027.      fprintf(stderr," TeX mode ");
  2028.      fflush(stderr);
  2029.      needCRLF = TRUE;
  2030.      lineno = 0;
  2031.      charno = 0;
  2032.      pfcnt = 0;
  2033.  
  2034.      while (!feof(fi))
  2035.         { /* read lines */
  2036.  
  2037.      lineno++;
  2038.      linecharno = charno;
  2039.      charno = readline(&lb,fi);
  2040.      dbp = lb.buffer;
  2041.      while( dbp = stpchr(dbp,'\\'))
  2042.         { /* some TeX command... */
  2043.          ebp = NULL;
  2044.          if(strncmp(dbp,"\\label",6) == 0)
  2045.             { /* \label */
  2046.          obp = &dbp[6];
  2047.          while(*obp == ' ') obp++;
  2048.          
  2049.          ebp = obp;
  2050.          while(*ebp != '\n' && *ebp != ' ') ebp++;
  2051.  
  2052.          if(ebp != NULL)
  2053.             { /* parse label */
  2054.              char c;
  2055.               /* \label name 
  2056.                * ^dbp       ^ebp
  2057.                *        ^obp
  2058.               */
  2059.               c = *ebp;
  2060.               *ebp = '\0';
  2061.               strcpy(nambuf,obp);
  2062.               *ebp = c;
  2063.  
  2064.               /* trim trailing whitespace */
  2065.  
  2066.               while(cp = rindex(nambuf,' ')) *cp = '\0';
  2067.  
  2068.               pfnote(nambuf, TRUE, dbp, ebp - dbp + 1, lineno, linecharno);
  2069.               pfcnt++;
  2070.             } /* parse label */
  2071.         } /* \label */
  2072.          dbp++;
  2073.         } /* some TeX command... */
  2074.     } /* read lines */
  2075.     }
  2076.  
  2077. /*
  2078.  * lisp tag functions
  2079.  * just look for (def or (DEF
  2080.  */
  2081.  
  2082. void L_funcs (fi)
  2083.      FILE *fi;
  2084. {
  2085.   lineno = 0;
  2086.   charno = 0;
  2087.   pfcnt = 0;
  2088.  
  2089.   while (!feof (fi))
  2090.     {
  2091.       lineno++;
  2092.       linecharno = charno;
  2093.       charno = readline (&lb, fi);
  2094.       dbp = lb.buffer;
  2095.       if (dbp[0] == '(' && 
  2096.       (dbp[1] == 'D' || dbp[1] == 'd') &&
  2097.         (dbp[2] == 'E' || dbp[2] == 'e') &&
  2098.           (dbp[3] == 'F' || dbp[3] == 'f'))
  2099.     {
  2100.       while (!isspace(*dbp)) dbp++;
  2101.       while (isspace(*dbp)) dbp++;
  2102.       L_getit();
  2103.     }
  2104.     }
  2105. }
  2106.  
  2107. void L_getit()
  2108. {
  2109.   register char *cp;
  2110.   char c;
  2111.   char nambuf[BUFSIZ];
  2112.  
  2113.   if (*dbp == 0) return;
  2114.   for (cp = dbp+1; *cp && *cp != '(' && *cp != ' '; cp++)
  2115.     continue;
  2116.   c = cp[0];
  2117.   cp[0] = 0;
  2118.   strcpy(nambuf, dbp);
  2119.   cp[0] = c;
  2120.   pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
  2121.   pfcnt++;
  2122. }
  2123.  
  2124. /****************************************************************************
  2125. *                                 initbuffer
  2126. * Inputs:
  2127. *       struct linebuffer * linebuffer: Line buffer structure to be
  2128. *                initialized
  2129. * Result: void
  2130. *       
  2131. * Effect: 
  2132. *       initializes a line buffer for use
  2133. ****************************************************************************/
  2134.  
  2135. void
  2136. initbuffer (linebuffer)
  2137.      struct linebuffer *linebuffer;
  2138. {
  2139.   linebuffer->size = 200;
  2140.   linebuffer->buffer = (char *) xmalloc (200);
  2141. }
  2142.  
  2143.  
  2144. /****************************************************************************
  2145. *                                  readline
  2146. * Inputs:
  2147. *       struct linebuffer * linebuffer: Line buffer to hold input line
  2148. *    FILE * stream: file from which to read
  2149. * Result: long
  2150. *    file position of end of read operation
  2151. * Effect: 
  2152. *     Read a line of text from `stream' into `linebuffer'.
  2153. *     Return the length of the line.  
  2154. ****************************************************************************/
  2155.  
  2156. long
  2157. readline (linebuffer, stream)
  2158.      struct linebuffer *linebuffer;
  2159.      reg FILE *stream;
  2160. {
  2161.   char *buffer = linebuffer->buffer;
  2162.   reg char *p = linebuffer->buffer;
  2163.   reg char *pend = p + linebuffer->size;
  2164.  
  2165.   while (1)
  2166.     {
  2167.       int c = getc (stream);
  2168.       if (p == pend)
  2169.     {
  2170.       buffer = (char *) xrealloc (buffer, linebuffer->size *= 2);
  2171.       p += buffer - linebuffer->buffer;
  2172.       pend += buffer - linebuffer->buffer;
  2173.       linebuffer->buffer = buffer;
  2174.     }
  2175.  
  2176.       /* If we have a NUL or LF, store a NUL at the end of the buffer
  2177.      and exit the loop
  2178.       */
  2179.  
  2180.       if (c < 0 || c == '\n')
  2181.     {
  2182.       *p = 0;
  2183.       break;
  2184.     }
  2185.  
  2186.      /* Otherwise, store the character and update the pointer */
  2187.  
  2188.       *p++ = c;
  2189.     }
  2190.  
  2191. #if debug
  2192.  printf("readline: |%s| => %ld\n", buffer, (long) (p-buffer));
  2193. #endif
  2194.   return ftell( stream);
  2195. }
  2196.  
  2197. /****************************************************************************
  2198. *                                   savestr
  2199. * Inputs:
  2200. *       char * cp: Pointer to string
  2201. * Result: char *
  2202. *       pointer to copy of the string originally pointed to by cp
  2203. * Effect: 
  2204. *       Creates a copy of the string pointed to by 'cp'
  2205. ****************************************************************************/
  2206.  
  2207. char *
  2208. savestr(cp)
  2209.      char *cp;
  2210. {
  2211.   register int len;
  2212.   register char *dp;
  2213.  
  2214.   len = strlen(cp);
  2215.   dp = (char *)xmalloc(len+1);
  2216.   strcpy(dp, cp);
  2217.   return (dp);
  2218. }
  2219.  
  2220.  
  2221. /****************************************************************************
  2222. *                                   rindex
  2223. * Inputs:
  2224. *       char * sp: Pointer to string
  2225. *    char c: Character of interest
  2226. * Result: char *
  2227. *       Pointer to last instance of c in sp, or NULL if no occurrence
  2228. *
  2229. *     Identical to v7 rindex, included for portability.
  2230. ****************************************************************************/
  2231.  
  2232.  
  2233. char *
  2234. rindex(sp, c)
  2235.      register char *sp, c;
  2236. {
  2237.   register char *r;
  2238.  
  2239.   r = NULL;
  2240.   do
  2241.     {
  2242.       if (*sp == c)
  2243.     r = sp;
  2244.     } while (*sp++);
  2245.   return(r);
  2246. }
  2247.  
  2248. /****************************************************************************
  2249. *                                    fatal
  2250. * Inputs:
  2251. *       char * s1: Printf control string with one %-arg
  2252. *    char * s2: Argument for printf control string
  2253. * Result: never returns
  2254. *
  2255. * Effect: 
  2256. *       Issues error message and exits with halt code 1
  2257. ****************************************************************************/
  2258.  
  2259. void fatal (s1, s2)
  2260.      char *s1, *s2;
  2261. {
  2262.   error (s1, s2);
  2263.   exit (1);
  2264. }
  2265.  
  2266. /****************************************************************************
  2267. *                                    error
  2268. * Inputs:
  2269. *       char * s1: Printf control string with one %-arg
  2270. *    char * s2: Argument for printf control string
  2271. * Result: void
  2272. *       
  2273. * Effect: 
  2274. *       Issues error message followed by newline
  2275. ****************************************************************************/
  2276.  
  2277. void error (s1, s2)
  2278.      char *s1, *s2;
  2279. {
  2280. #ifdef CTAGS
  2281.   printf ("ctags: ");
  2282. #else
  2283.   printf ("etags: ");
  2284. #endif
  2285.   printf (s1, s2);
  2286.   printf ("\n");
  2287. }
  2288.  
  2289. /****************************************************************************
  2290. *                                   concat
  2291. * Inputs:
  2292. *       char * s1, *s2, *s3: Three string pointers
  2293. * Result: char *
  2294. *       New string which is concatenation of s1, s2, s3
  2295. * Effect: 
  2296. *       Allocates new string and copies s1, s2, s3 into it
  2297. ****************************************************************************/
  2298.  
  2299. char *
  2300. concat (s1, s2, s3)
  2301.      char *s1, *s2, *s3;
  2302. {
  2303.   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  2304.   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
  2305.  
  2306.   strcpy (result, s1);
  2307.   strcpy (result + len1, s2);
  2308.   strcpy (result + len1 + len2, s3);
  2309.   *(result + len1 + len2 + len3) = 0;
  2310.  
  2311.   return result;
  2312. }
  2313.  
  2314.  
  2315. /****************************************************************************
  2316. *                                   xmalloc
  2317. * Inputs:
  2318. *       unsigned size: Size of storage to allocate
  2319. * Result: char *
  2320. *       Newly allocated storage.
  2321. * Effect: 
  2322. *       Like malloc, except takes fatal error and exits if memory exhausted
  2323. ****************************************************************************/
  2324.  
  2325. char *
  2326. xmalloc (size)
  2327.      unsigned size;
  2328. {
  2329.   char * result = malloc (size);
  2330.   if (!result)
  2331.     fatal ("virtual memory exhausted", 0);
  2332.   return result;
  2333. }
  2334.  
  2335. /****************************************************************************
  2336. *                                  xrealloc
  2337. * Inputs:
  2338. *       char * ptr: Pointer to space containing information
  2339. *    unsigned size: Size we really want
  2340. * Result: 2
  2341. *       2
  2342. * Effect: 
  2343. *       like realloc but gives fatal error and exits if memory exhausted
  2344. *
  2345. *       jmn: I can't find this anywhere in my Unix manuals or Lattice C
  2346. *    manual, but judging from its use it is supposed to allocate
  2347. *    a chunk of storage of size 'size' and return a pointer to it.
  2348. *    The presumption is that if it can tail-allocate or otherwise
  2349. *    avoid copying it will do so.  I have written a realloc for
  2350. *    MS-DOS which has to copy
  2351. ****************************************************************************/
  2352.  
  2353. char *
  2354. xrealloc (ptr, size)
  2355.      char *ptr;
  2356.      unsigned size;
  2357. {
  2358.   char * result = realloc (ptr, size);
  2359.   if (!result)
  2360.     fatal ("virtual memory exhausted");
  2361.   return result;
  2362. }
  2363.  
  2364.  
  2365. /* The text which follows is the Epsilon Tags.e file.  Extract it and
  2366.    use it with Epsilon
  2367. */
  2368. #if 0
  2369.  
  2370. /*****************************************************************************
  2371. *           Change Log
  2372. *  Date     | Change
  2373. *-----------+-----------------------------------------------------------------
  2374. *  3-Jun-86 | Created
  2375. *  4-Jun-86 | Use absolute for tagname in load_tags
  2376. *  4-Jun-86 | [313.43] Made non-resident; copied goodies to tags1
  2377. *****************************************************************************/
  2378.  
  2379. #include "eel.h"
  2380. #include "boolean.h"
  2381.  
  2382. boolean tags_loaded = false;
  2383.  
  2384. /****************************************************************************
  2385. *                                    tags
  2386. * Effect: 
  2387. *       Loads and executes the tags library
  2388. ****************************************************************************/
  2389.  
  2390. command tags() on reg_tab[ALT('.')]
  2391.     {
  2392.      if(!tags_loaded)
  2393.         { /* load it */
  2394.      load_library("tags1");
  2395.      tags_loaded = true;
  2396.      tags_1();
  2397.     } /* load it */
  2398.      else
  2399.     tags_1();
  2400.     }
  2401. /* End of TAGS.E */
  2402. #endif
  2403.  
  2404. /* The following file is the Epsilon code for TAGS1.E.  Extract it and use
  2405.    it with Epsilon
  2406. */
  2407. #if 0
  2408.  
  2409. /*****************************************************************************
  2410. *           Change Log
  2411. *  Date     | Change
  2412. *-----------+-----------------------------------------------------------------
  2413. *  3-Jun-86 | Created
  2414. *  4-Jun-86 | Use absolute for tagname in load_tags
  2415. *  4-Jun-86 | [313.43] Made nonresident; entry point is tags_1
  2416. * 10-Jul-86 | [313.46] Restore point after grab of search string
  2417. * 10-Jul-86 | [313.46] Make sure all 'error' calls after reading in tags
  2418. *           | properly restore oldbuf and point
  2419. *****************************************************************************/
  2420.  
  2421. /* Parse the tags file looking for the input name; position the user in that
  2422.    file looking at the name.
  2423.  
  2424.    Limitations: If the filename in the tags file is not a full path, it
  2425.    is interpreted relative to the current connected directory.
  2426.  
  2427. */
  2428.  
  2429. #include "eel.h"
  2430. #include "boolean.h"
  2431.  
  2432. #define debug_tags false
  2433.  
  2434. /****************************************************************************
  2435. *                               debug_tags_msg
  2436. * Inputs:
  2437. *       char * msg: Message to print
  2438. * Effect: 
  2439. *       Puts it in the debug buffer
  2440. ****************************************************************************/
  2441.  
  2442. #if debug_tags
  2443. debug_tags_msg(msg)
  2444.      char * msg;
  2445.     {
  2446.      char * oldbuf = bufname;
  2447.      if(!exist("debug"))
  2448.         { /* make debug buffer */
  2449.      zap("debug");
  2450.     } /* make debug buffer */
  2451.  
  2452.      bufname = "debug";
  2453.      point = size();
  2454.      stuff(msg);
  2455.      bufname = oldbuf;
  2456.     }
  2457. #endif
  2458.  
  2459. /****************************************************************************
  2460. *                                  load_tags
  2461. * Result: boolean
  2462. *       true if tags file found
  2463. *    false if tags file not found
  2464. * Effect: 
  2465. *       loads the tags file
  2466. ****************************************************************************/
  2467.  
  2468. boolean load_tags()
  2469.     {
  2470.      char * s;
  2471.      char tagname[80];
  2472.  
  2473.      s = buffer_list(1);
  2474.  
  2475.      do
  2476.         { /* see if buffer exists */
  2477.      bufname = s;
  2478.      if(strcmp(filename,"tags")==0)
  2479.         { /* found it */
  2480.           /* What we do here is see if the file timestamp is the same;
  2481.          if not, we reload the tags file
  2482.           */
  2483.           struct file_info f_disk;
  2484.  
  2485.           struct time_info tf, tb;
  2486.  
  2487.           if(check_file("tags",&f_disk))
  2488.              { /* lost tags */
  2489.           /* This is probably because we have shifted to another
  2490.              directory, or tags failed after partially erasing the
  2491.              old file, so what we do here is assume the 'tags' buffer
  2492.              we are in is valid
  2493.           */
  2494. #if debug_tags
  2495.           debug_tags_msg("load_tags: lost tags file, use existing one\n");
  2496. #endif
  2497.           
  2498.           return true;
  2499.          } /* lost tags */
  2500.  
  2501.          if(f_disk.year == f_info.year &&
  2502.         f_disk.month == f_info.month &&
  2503.         f_disk.day == f_info.day &&
  2504.         f_disk.hour == f_info.hour &&
  2505.         f_disk.minute == f_info.minute &&
  2506.         f_disk.second == f_info.second)
  2507.            { /* same file */
  2508. #if debug_tags
  2509.             debug_tags_msg("load_tags: tags file is the same\n");
  2510. #endif
  2511.             return true;
  2512.            } /* same file */
  2513.         
  2514.         /* At this point, the timestamp of 'tags' on disk and the
  2515.            timestamp of 'tags' in the buffer are different
  2516.         */
  2517.  
  2518. #if debug_tags
  2519.        { /* reverting */
  2520.         char msg[80];
  2521.             sprintf(msg,
  2522.             "load_tags: reverted 'tags' file.  bufname = %s\n",
  2523.                 bufname);
  2524.         debug_tags_msg(msg);
  2525.        } /* reverting */
  2526. #endif
  2527.         read_file("tags");
  2528.         point = 0;
  2529.         return true;
  2530.         } /* found it */
  2531.     } /* see if buffer exists */
  2532.           while (s = buffer_list(0));
  2533.  
  2534.      /* We didn't find a tags buffer; create one and try to read in a
  2535.     file called 'tags'
  2536.      */
  2537.  
  2538.      zap("tags");
  2539.      bufname = "tags";
  2540.      strcpy(tagname,"tags");
  2541.      absolute(tagname);
  2542.      read_file(tagname);
  2543.          
  2544. #if debug_tags
  2545.      debug_tags_msg("load_tags: created 'tags' buffer and loaded it\n");
  2546. #endif
  2547.      return true;
  2548.     }
  2549.  
  2550. /****************************************************************************
  2551. *                               locate_pattern
  2552. * Inputs:
  2553. *       char * fname: File name in which to locate pattern
  2554. *    char * pattern: Pattern to find
  2555. * Result: boolean
  2556. *       true if pattern found
  2557. *    false if pattern not found
  2558. * Effect: 
  2559. *    true:
  2560. *        Reads the file into a buffer if required.
  2561. *           Leaves the buffer positioned at the line containing the pattern
  2562. *    false:
  2563. *        Reads the file into a buffer if required.
  2564. *        Leaves the buffer unchanged (at start if just read in)
  2565. ****************************************************************************/
  2566.  
  2567. locate_pattern(fname,pattern)
  2568.     char * fname;
  2569.     char * pattern;
  2570.     {
  2571.      int oldpoint;
  2572.      char * oldbuf = bufname;
  2573.      int orig_num = window_number;
  2574.  
  2575.      window_number = 0;
  2576.      /* We search to see if the file is displayed in any window; in that
  2577.     case we want to go to the window to find it
  2578.      */
  2579.  
  2580.      do
  2581.         { /* search for currently on screen */
  2582.      if(strcmp(filename,fname) == 0)
  2583.         orig_num = window_number;
  2584.      window_number++;
  2585.     } /* search for currently on screen */
  2586.          while (window_number);
  2587.  
  2588.      window_number = orig_num;
  2589.  
  2590. #if debug_tags
  2591.        { /* log it */
  2592.          char msg[120];
  2593.          sprintf(msg,"locate_pattern(\"%s\",\"%s\")\n",fname,pattern);
  2594.          debug_tags_msg(msg);
  2595.        } /* log it */
  2596. #endif
  2597.         
  2598.      find_it(fname);
  2599.  
  2600.      oldpoint = point;
  2601.  
  2602.      point = 0;
  2603.  
  2604.      if(!re_search(FORWARD,pattern))
  2605.         { /* failed */
  2606.      point = oldpoint;
  2607.      sayput("Couldn't find tag for \"%s\"",pattern);
  2608.      return false;
  2609.     } /* failed */
  2610.  
  2611.      to_begin_line();
  2612.      
  2613.      return true;
  2614.     }
  2615.  
  2616. /****************************************************************************
  2617. *                                   tags_1
  2618. * Effect: 
  2619. *       Loads the 'tags' file, searches it for the tag; if the tag is
  2620. *    found, go open the file in the current window and use the pattern
  2621. *    to locate the function.  If not, complain.
  2622. ****************************************************************************/
  2623.  
  2624. command tags_1() on reg_tab[ALT('.')]
  2625.     {
  2626.      int oldpoint = point;
  2627.      int start;
  2628.      char search_string[80];
  2629.      char search_word[80];
  2630.      char search_msg[120];
  2631.      char * oldbuf = bufname;
  2632.      char fname[80];
  2633.      int filestart;
  2634. #define pattern search_msg
  2635.      int pattern_start;
  2636.  
  2637.      if(!parse_string(1, word_pattern, search_word)) strcpy(search_word,"");
  2638.  
  2639.      strcpy(search_msg,"Tag [");
  2640.      strcat(search_msg,search_word);
  2641.      strcat(search_msg,"] :");
  2642.      point = oldpoint;
  2643.      get_string(&search_string[1],search_msg);
  2644.      check_abort();
  2645.      iter = 0;
  2646.      if(search_string[1] == '\0') strcpy(&search_string[1],search_word);
  2647.  
  2648.      /* Now see if we have a tag at all */
  2649.  
  2650.      if(search_string[1] == '\0') return;
  2651.  
  2652.      /* Now make up an re_search string of the form
  2653.         ^word<space>
  2654.     to find the search string in the tags file
  2655.      */
  2656.  
  2657.      search_string[0] = '^';
  2658.      strcat(search_string,"\t");
  2659.  
  2660.      if(!load_tags()) return;        /* couldn't find tags file */
  2661.  
  2662.      /* If we loaded tags sucessfully, we are now in the tags buffer */
  2663.  
  2664.      point = 0;
  2665.  
  2666.      if(!re_search(FORWARD,search_string))
  2667.         { /* no such tag */
  2668.      bufname = oldbuf;
  2669.      point = oldpoint;
  2670.      error("No tag \"%s\" found",search_string);
  2671.     } /* no such tag */
  2672.  
  2673.      /* If we get here, the search was successful */
  2674.  
  2675.      /* We have searched for a line of the form
  2676.         C_entries\tjunk.c\t/^C_entries ()$/
  2677.                    ^we are here
  2678.      */
  2679.      filestart = point;
  2680.  
  2681.      if(!search(FORWARD,"\t"))
  2682.         { /* bad format */
  2683.      bufname = oldbuf;
  2684.      point = oldpoint;
  2685.      error("internal error: bad format in tags file, no tab after filename");
  2686.     } /* bad format */
  2687.      
  2688.      /* We are now after the filename 
  2689.         C_entries\tjunk.c\t/^C_entries ()$/
  2690.                            ^we are here
  2691.      */
  2692.  
  2693.      /* Grab the file name */
  2694.  
  2695.      grab(filestart,point - 1, fname);
  2696.  
  2697.      absolute(fname);            /* make it absolute */
  2698.  
  2699.      search(FORWARD,"/");
  2700.      pattern_start = point;
  2701.      to_end_line();
  2702.      search(BACKWARD,"/");
  2703.  
  2704.      grab(pattern_start,point, pattern);
  2705.  
  2706.      point = oldpoint;        /* restore point */
  2707.  
  2708.      if(!locate_pattern(fname,pattern))
  2709.         { /* no match */
  2710.      bufname = oldbuf;
  2711.      point = oldpoint;
  2712.      error("Couldn't find \"%s\" in \"%s\"",pattern, fname);
  2713.     } /* no match */
  2714.  
  2715.      /* bufname is now the buffer in which the tag was found */
  2716.  
  2717.      to_buffer(bufname);
  2718.      
  2719.     }
  2720. /* End of TAGS1.E */
  2721. #endif
  2722.  
  2723. /* The following code is the code for Epsilon load_library.  Extract it and
  2724.    use it with Epsilon*/
  2725. #if 0
  2726.  
  2727. /****************************************************************************
  2728. *                                load_library (jmn)
  2729. * Inputs:
  2730. *       char * name: Library file name
  2731. * Result: boolean
  2732. *       true if library loaded sucessfully
  2733. *    false if it failed for some reason
  2734. * Effect: 
  2735. *       Loads the bytes from the library file.  Uses the library path
  2736. *    as first choice.
  2737. *
  2738. *    Library path is based upon the variable ELIB
  2739. ****************************************************************************/
  2740.  
  2741. boolean load_library(name)
  2742.     char * name;
  2743.     {
  2744.      char filename[80];
  2745.      char * elib;
  2746.  
  2747.      elib = getenv("ELIB");
  2748.  
  2749. #if debug_load
  2750.      say("ELIB=%s",elib);
  2751. #endif
  2752.  
  2753.      if(elib != NULL && strlen(elib) > 0)
  2754.         { /* try library path */
  2755. #if debug_load
  2756.      char * oldbuf = bufname;
  2757.      zap("debug");
  2758.      to_buffer("debug");
  2759.      bprintf("elib = %s.",elib);
  2760.      to_buffer(oldbuf);
  2761. #endif
  2762.      strcpy(filename,elib);
  2763.      strcat(filename,name);
  2764.      strcat(filename,".b");
  2765.      if(!check_file(filename, (struct file_info *) 0))
  2766.         { /* file exists */
  2767.          load_commands(filename);    /* may abort to top-level */
  2768.          return true;        /* all done */
  2769.         } /* file exists */
  2770. #if debug_load
  2771.      else
  2772.         { /* failed */
  2773.          to_buffer("debug");
  2774.          bprintf("Cannot find %s.",filename);
  2775.          to_buffer(oldbuf);
  2776.         } /* failed */
  2777. #endif
  2778.     } /* try library path */
  2779.      
  2780.      /* The file does not exist on the elib path, or there is no
  2781.         elib path 
  2782.      */
  2783.  
  2784.      /* Try the default search path */
  2785.  
  2786.      elib = lookpath(name);
  2787.  
  2788.      if(elib != NULL)
  2789.         { /* found on path */
  2790.      load_commands(elib);
  2791.     } /* found on path */
  2792.      else
  2793.         { /* use default */
  2794.      load_commands(name);    /* default path; may abort to top-level */
  2795.     } /* use default */
  2796.  
  2797.      return true;
  2798.     }
  2799. /* End of load_library */
  2800. #endif
  2801.  
  2802. /* This file is the 'makefile' which can be used to build 'tags'.  Be sure
  2803.    to read it over carefully since it is tuned to my development environment.
  2804.    I use UniPress 'psmake'.  The file 'go.bat' is included later
  2805.  
  2806.    IMPORTANT: Replace all instances of "%#" with "#" because the presence
  2807.    of the # in column 1 produces a very sick Lattice C compiler (it runs
  2808.    out of memory with 594K available).
  2809. */
  2810.  
  2811. #if 0
  2812. %#*****************************************************************************
  2813. %#    makefile for TAGS
  2814. %#*****************************************************************************
  2815. %#     Change Log
  2816. %#   Date    | Change
  2817. %#-----------+----------------------------------------------------------------
  2818. %#  4-Jul-86 | Created
  2819. %#*****************************************************************************
  2820.  
  2821. %# Standard prolog
  2822.  
  2823. %# This declaration defines the location of the C compiler
  2824.  
  2825. COMPILER = c:\lc\
  2826.  
  2827. %# This declaration defines the device on which the intermediate files 
  2828. %# (.Q files) are written.  If a RAMdisk (e.g., E:) is selected, the
  2829. %# compilation will run much faster
  2830.  
  2831. VDISK=e:
  2832.  
  2833. %# This declaration defines the device on which the output file is written.
  2834.  
  2835. LDISK=d:
  2836.  
  2837. %# The source extensions
  2838.  
  2839. SRCEXT = .c .asm
  2840.  
  2841. %#############################################################################
  2842. %# Compilation rules.
  2843. %#############################################################################
  2844.  
  2845. %# Rules for compiling .c source files
  2846. %# Note the use of the virtual disk 'm' for the memory model
  2847. %# The association must be made in the 'go.bat' file
  2848.  
  2849. .c.obj :
  2850.     del $*.obj
  2851.     $(COMPILER)lc1 -o$(VDISK) -ms -d -n -ic:\lc\ -cstu -im: $< 
  2852.     $(COMPILER)lc2  -o$(LDISK) $(VDISK)$*
  2853.     if exist $*.obj goto $*
  2854.     echo * * *    Compilation of '$*' failed >> nolink
  2855.     :$*
  2856.  
  2857. .c.tag :
  2858.     tags -m -u $<
  2859.  
  2860. .asm.obj :
  2861.     del $*.obj
  2862.     masm $*,$*,$*;
  2863.     if exist $*.obj goto $*
  2864.     echo * * *    Assembly of $* failed >> nolink
  2865.     :$*
  2866.  
  2867.  
  2868.  
  2869. %##############################################################################
  2870. %#    Object file specifications
  2871. %##############################################################################
  2872.  
  2873. TAGS =m:c.obj tags.obj 
  2874.  
  2875. %# The name of the linker files
  2876.  
  2877. T=tags.lnk
  2878.  
  2879.  
  2880. %##############################################################################
  2881. %# The files
  2882. %##############################################################################
  2883. %#
  2884. %# Note the use of the dummy name (xxxxx) which never exists, but captures
  2885. %# the dependency of the two result files, tags and tags.exe.
  2886.  
  2887. xxxxx: tags. tags.exe
  2888.  
  2889. tags.: tags.tag 
  2890.  
  2891. TAGS.EXE: $(TAGS) $T makefile
  2892.     if not exist nolink goto oklink
  2893.     echo  ***** Compilation errors exist *****
  2894.     type nolink
  2895.     goto nolink
  2896.     :oklink
  2897.     version version.c version.ver
  2898.     link @$(T),tags,tags,m:ctools+m:ctools2+m:lcm+m:lc/map/line/segments:256
  2899.     :nolink
  2900.  
  2901.  
  2902. %# The linker file.  Note that it depends on 'makefile' so that it is
  2903. %# always updated.  If you add more OBFILESn, all but the last must
  2904. %# include the + symbol at the end of line
  2905.  
  2906. $T : makefile
  2907.     echo $(TAGS) > $T
  2908.  
  2909. tags.obj :   l:stdio.h    l:ctype.h    l:string.h    l:dos.h
  2910.  
  2911. tags.tag : tags.c
  2912.  
  2913.  
  2914. #endif
  2915.